/*************************************************************************
 * Copyright 2008 David Almilli
 *  
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *      
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 **************************************************************************/
package com.almilli.catnip.server;

import com.almilli.catnip.beacon.TiVoBeacon;
import com.almilli.catnip.logging.Log;
import com.almilli.catnip.logging.LogFactory;
import com.almilli.catnip.web.HmeHost;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.servlet.ServletException;
import org.apache.catalina.Container;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.Server;
import org.apache.catalina.Service;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;

/**
 * The <code>HmeValve</code> class is used to enable HME applications in a web application. It
 * will change the configuration for each Host within it's Container so that it will read an
 * additional hme.xml file where HME applications are defined and configured.  It will also add
 * all the known ports for the container to the TiVoBeacon.  The HmeValve can only be used inside
 * an Engine or Host container.
 * @author almilli
 */
public class HmeValve extends ValveBase implements HmeHost {
	private static final Log log = LogFactory.getLog(HmeValve.class);
	
	private TiVoBeacon beacon;
	private List<Integer> hmePorts = new ArrayList<Integer>();

	@Override
	public void invoke(Request request, Response response) throws IOException, ServletException {
		getNext().invoke(request, response);
	}

	@Override
	public ObjectName preRegister(MBeanServer mserver, ObjectName name) throws Exception {
		Container container = getContainer();
		if (!(container instanceof Host)) {
			throw new IllegalStateException("HmeValve can only be nested in a Host element");
		}
		
		//override the config for all hosts in this container
		registerAllHosts(container);
		
		Engine engine = ContextUtils.getEngine(getContainer());
		
		if (engine != null) {
			Service service = engine.getService();
			if (service != null) {
				Server server = service.getServer();
		
				beacon = ContextUtils.getTiVoBeacon(server);
				if (beacon != null) {
					if (log.isDebugEnabled()) {
						log.debug("Registering ports on TiVoBeacon...");
					}
					for (Connector conn : service.findConnectors()) {
						if (conn.getProtocolHandler().getClass().getName().endsWith(".HmeProtocol")) {
							//this is an HME port, register it
							int port = conn.getPort();
							hmePorts.add(port);
						}
					}
                    if (log.isInfoEnabled()) {
                        log.info("Registering host on TiVoBeacon.");
                    }
                    beacon.addHost(this);
				} else {
					log.warn("Cannot find TiVoBeacon to register HME applications");
				}
			} else {
				log.error("The Engine does not have a Service.  Tomcat is misconfigured.");
			}
		} else {
			log.error("Cannot find engine.  The HmeValve must be nested somwhere inside the Engine.");
		}
		
		return super.preRegister(mserver, name);
	}

	
	public void registerAllHosts(Container container) {
		if (container instanceof Host) {
			Host host = (Host)container;
			host.setConfigClass(HmeConfig.class.getName());
		} else {
			//find children
			for (Container child : container.findChildren()) {
				registerAllHosts(child);
			}
		}
	}

	@Override
    public void preDeregister() throws Exception {
		
		if (beacon != null) {
			if (log.isDebugEnabled()) {
				log.debug("Unregistering host on TiVoBeacon...");
			}
            beacon.removeHost(this);
            beacon = null;
		}

		super.preDeregister();
    }

    public List<Integer> getPorts() {
        return Collections.unmodifiableList(hmePorts);
    }
}
